package com.sky.service.common.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.*;
import com.sky.dto.CategoryDTO;
import com.sky.dto.CategoryPageQueryDTO;
import com.sky.exception.DeletionNotAllowedException;
import com.sky.exception.ParameterInvalidException;
import com.sky.exception.ParameterIsNullException;
import com.sky.mapper.admin.CategoryMapper;
import com.sky.mapper.admin.DishMapper;
import com.sky.mapper.admin.SetmealMapper;
import com.sky.pojo.Category;
import com.sky.result.PageResult;
import com.sky.service.common.CategoryService;
import com.sky.vo.DishVO;
import com.sky.vo.SetmealVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 分类业务实现
 *
 * @author zgp
 * @version V1.0
 * @date 2025-01-13 19:43
 */
@Service
@Slf4j
public class CategoryServiceImpl implements CategoryService {
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private SetmealMapper setmealMapper;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public PageResult<Category> getPageList(CategoryPageQueryDTO categoryPageQueryDTO) {
        int pageSize = categoryPageQueryDTO.getPageSize();
        int page = categoryPageQueryDTO.getPage();
        // 校验参数
        if (pageSize < BaseServiceConstant.ZERO || page < BaseServiceConstant.ZERO) {
            throw new ParameterInvalidException(MessageConstant.PAGE_PARAMETER_IS_INVALID);
        }
        Integer type = categoryPageQueryDTO.getType();
        if (Objects.nonNull(type) && !type.equals(CategoryTypeConstant.DISH_TYPE) && !type.equals(CategoryTypeConstant.SETMEAL_TYPE)) {
            throw new ParameterInvalidException(MessageConstant.CATEGORY_TYPE_IS_INVALID);
        }
        // 处理业务
        Category category = new Category();
        BeanUtils.copyProperties(categoryPageQueryDTO, category);
        PageHelper.startPage(page, pageSize);
        Page<Category> result = categoryMapper.selectPageList(category);
        // 封装结果
        return new PageResult<>(result.getTotal(), result.getResult());
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void addCategory(CategoryDTO categoryDTO) {
        // 参数校验
        if (StringUtils.isEmpty(categoryDTO.getName())) {
            throw new ParameterIsNullException(MessageConstant.CATEGORY_NAME_IS_NULL);
        }
        Integer type = categoryDTO.getType();
        if (Objects.isNull(type)) {
            throw new ParameterIsNullException(MessageConstant.CATEGORY_NAME_IS_NULL);
        }
        if (!type.equals(CategoryTypeConstant.DISH_TYPE) && !type.equals(CategoryTypeConstant.SETMEAL_TYPE)) {
            throw new ParameterInvalidException(MessageConstant.CATEGORY_TYPE_IS_INVALID);
        }
        if (Objects.isNull(categoryDTO.getSort())) {
            throw new ParameterIsNullException(MessageConstant.ADD_CATEGORY_SORT_PARAM_IS_NULL);
        }
        Category category = new Category();
        BeanUtils.copyProperties(categoryDTO, category);
        category.setStatus(StatusConstant.DISABLE);
        // 新增
        categoryMapper.insert(category);
        try {
            // 清除redis分类缓存，保证redis和数据库数据的一致性
            redisTemplate.delete(RedisConstant.CATEGORY_CACHE_PREFIX + category.getType());
        } catch (Exception e) {
            log.error("清除Redis分类缓存失败，key={}", RedisConstant.CATEGORY_CACHE_PREFIX + category.getType(), e);
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void deleteCategory(Integer id) {
        if (Objects.isNull(id)) {
            throw new ParameterIsNullException(MessageConstant.ID_IS_NULL);
        }
        // 校验当前分类是否关联菜品或套餐，有关联不允许删除
        List<DishVO> dishs = dishMapper.selectByCategoryIdAndStatus(id, null);
        List<SetmealVO> setmeals = setmealMapper.selectByCategoryIdAndStatus(id, null);
        if (CollUtil.isNotEmpty(dishs)) {
            throw new DeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_DISH);
        }
        if (CollUtil.isNotEmpty(setmeals)) {
            throw new DeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_SETMEAL);
        }
        // 查询当前id的分类信息，用于获取type构建redis key
        Category category = categoryMapper.selectById(id.longValue());
        // 删除分类
        categoryMapper.deleteById(id);
        //  使用try-catch：Redis 缓存清除失败，会导致事务回滚，避免其影响数据库操作的提交
        try {
            // 清除redis分类缓存，保证redis和数据库数据的一致性
            redisTemplate.delete(RedisConstant.CATEGORY_CACHE_PREFIX + category.getType());
        } catch (Exception e) {
            log.error("清除Redis分类缓存失败，key={}", RedisConstant.CATEGORY_CACHE_PREFIX + category.getType(), e);
        }
    }

    @Override
    public List<Category> getCategoryByType(Integer type) {
        // 校验参数
        if (Objects.isNull(type)) {
            throw new ParameterIsNullException(MessageConstant.CATEGORY_NAME_IS_NULL);
        }
        if (!type.equals(CategoryTypeConstant.DISH_TYPE) && !type.equals(CategoryTypeConstant.SETMEAL_TYPE)) {
            throw new ParameterInvalidException(MessageConstant.CATEGORY_TYPE_IS_INVALID);
        }
        // 查询分类：改用redis缓存分类信息
        // 先查询redis缓存
        List<Category> categories = null;
        try {
            categories = (List<Category>) redisTemplate.opsForValue().get(RedisConstant.CATEGORY_CACHE_PREFIX + type);
        } catch (Exception e) {
            log.error("查询Redis分类缓存失败，key={}", RedisConstant.CATEGORY_CACHE_PREFIX + type, e);
        }
        if (CollUtil.isEmpty(categories)) {
            // redis分类缓存为空，从数据库查询
            categories = categoryMapper.selectByType(type);
            try {
                redisTemplate.opsForValue().set(RedisConstant.CATEGORY_CACHE_PREFIX + type, categories, 1, TimeUnit.DAYS);
            } catch (Exception e) {
                log.error("Redis分类数据缓存失败,，key={}", RedisConstant.CATEGORY_CACHE_PREFIX + type, e);
            }
        }
        return categories;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void enableOrDisableCategory(Integer status, Long id) {
        // 校验参数
        if (Objects.isNull(id)) {
            throw new ParameterIsNullException(MessageConstant.ID_IS_NULL);
        }
        if (Objects.isNull(status)) {
            throw new ParameterIsNullException(MessageConstant.CATEGORY_NAME_IS_NULL);
        }
        if (!status.equals(StatusConstant.DISABLE) && !status.equals(StatusConstant.ENABLE)) {
            throw new ParameterInvalidException(MessageConstant.CATEGORY_STATUS_PARAM_IS_NULL);
        }
        // 封装参数
        Category category = new Category();
        category.setId(id);
        category.setStatus(status);
        // 修改
        categoryMapper.updateCategoryById(category);
        // 清空redis缓存
        Category categoryById = categoryMapper.selectById(id);
        try {
            redisTemplate.delete(RedisConstant.CATEGORY_CACHE_PREFIX + categoryById.getType());
        } catch (Exception e) {
            log.error("清除Redis分类缓存失败,，key={}", RedisConstant.CATEGORY_CACHE_PREFIX + categoryById.getType(), e);
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateCategoryById(CategoryDTO categoryDTO) {
        // 参数校验
        if (Objects.isNull(categoryDTO.getId())) {
            throw new ParameterIsNullException(MessageConstant.ID_IS_NULL);
        }
        if (StringUtils.isEmpty(categoryDTO.getName())) {
            throw new ParameterIsNullException(MessageConstant.CATEGORY_NAME_IS_NULL);
        }
        if (Objects.isNull(categoryDTO.getSort())) {
            throw new ParameterIsNullException(MessageConstant.CATEGORY_STATUS_PARAM_IS_NULL);
        }
        Category category = new Category();
        BeanUtils.copyProperties(categoryDTO, category);
        // 修改分类数据
        categoryMapper.updateCategoryById(category);

        // 清除redis缓存
        try {
            redisTemplate.delete(RedisConstant.CATEGORY_CACHE_PREFIX + category.getType());
        } catch (Exception e) {
            log.error("清除Redis分类缓存失败,，key={}", RedisConstant.CATEGORY_CACHE_PREFIX + category.getType(), e);
        }
    }

    @Override
    public List<Category> getEnableStatusCategorys() {
        List<Category> resultCategories = new ArrayList<>();
        // 从缓存查询菜品分类数据
        List<Category> categories = getCategorysCache(RedisConstant.CATEGORY_CACHE_PREFIX + CategoryTypeConstant.DISH_TYPE);
        // 如果缓存为空或解析失败，从数据库查询
        if (CollUtil.isEmpty(categories)) {
            categories = categoryMapper.selectByType(CategoryTypeConstant.DISH_TYPE);
            // 将查询结果写入缓存
            try {
                redisTemplate.opsForValue().set(RedisConstant.CATEGORY_CACHE_PREFIX + CategoryTypeConstant.DISH_TYPE, categories, 1, TimeUnit.DAYS);
            } catch (Exception e) {
                log.error("写入Redis缓存失败, key={}", RedisConstant.CATEGORY_CACHE_PREFIX + CategoryTypeConstant.DISH_TYPE, e);
            }
        }
        // 过滤启用状态的菜品分类
        resultCategories.addAll(categories.stream().filter(o -> StatusConstant.ENABLE.equals(o.getStatus())).toList());

        // 从缓存查询套餐分类数据
        categories.clear();
        categories = getCategorysCache(RedisConstant.CATEGORY_CACHE_PREFIX + CategoryTypeConstant.SETMEAL_TYPE);
        // 如果缓存为空或解析失败，从数据库查询
        if (CollUtil.isEmpty(categories)) {
            categories = categoryMapper.selectByType(CategoryTypeConstant.SETMEAL_TYPE);
            // 将查询结果写入缓存
            try {
                redisTemplate.opsForValue().set(RedisConstant.CATEGORY_CACHE_PREFIX + CategoryTypeConstant.SETMEAL_TYPE, categories, 1, TimeUnit.DAYS);
            } catch (Exception e) {
                log.error("写入Redis缓存失败, key={}", RedisConstant.CATEGORY_CACHE_PREFIX + CategoryTypeConstant.SETMEAL_TYPE, e);
            }
        }
        // 过滤启用状态的套餐分类
        resultCategories.addAll(categories.stream().filter(o -> StatusConstant.ENABLE.equals(o.getStatus())).toList());
        return resultCategories.stream().sorted(Comparator.comparing(Category::getSort)).toList();
    }

    /**
     * 从redis缓存总查询分类数据
     *
     * @param key key值
     * @return 查询结果
     */
    private List<Category> getCategorysCache(String key) {
        Object result = null;
        List<Category> categories = null;
        try {
            result = redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("查询Redis分类缓存失败,，key={}", key, e);
        }
        // 判断 objectDishs 是否为 null
        if (ObjectUtil.isNotNull(result)) {
            try {
                // 解析缓存数据
                categories = JSON.parseArray(result.toString(), Category.class);
            } catch (Exception e) {
                log.error("解析Redis缓存数据失败, key={}", RedisConstant.CATEGORY_CACHE_PREFIX + CategoryTypeConstant.DISH_TYPE, e);
            }
        }
        return categories;
    }
}
